#region File Description
//-----------------------------------------------------------------------------
// GameplayScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Serialization;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using DPSF;
using DPSF.ParticleSystems;
#endregion

namespace BeatPong
{
    /// <summary>
    /// This screen implements the actual game logic. It is just a
    /// placeholder to get the idea across: you'll probably want to
    /// put some more interesting gameplay in here!
    /// </summary>
    class GameplayScreen : GameScreen
    {
        #region Fields

        ContentManager content;

        Beat previousBeat = new Beat();

        Random random = new Random();
        SpriteBatch spriteBatch;
        Rectangle viewportRect;
        GameObject ball;
        GameObject player1paddle;
        GameObject player2paddle;
        KeyboardState previousKeyboardState = Keyboard.GetState();
        public GameSong song;
        Queue<TimeSpan> beats;

        int player1score = 0;
        int player2score = 0;
        SpriteFont font;
        Vector2 scoreDrawPoint = new Vector2(50.0f, 0.1f);
        float paddleSpeed = 10.0f;
        float ballSpeed = 26.0f;
        float maxVerticalBallVelocity = 15.0f;
        float distance;
        ParticleSystemManager particleSystemManager = new ParticleSystemManager();
        SmokeParticleSystem smokeParticleSystem;

        Dictionary<DefaultSpriteParticleSystem, DateTime> particleSystemEndTimes;

        #endregion

        #region Initialization


        /// <summary>
        /// Constructor.
        /// </summary>
        public GameplayScreen()
        {
            TransitionOnTime = TimeSpan.FromSeconds(1.5);
            TransitionOffTime = TimeSpan.FromSeconds(0.5);

            particleSystemEndTimes = new Dictionary<DefaultSpriteParticleSystem, DateTime>();
        }


        /// <summary>
        /// Load graphics content for the game.
        /// </summary>
        public override void LoadContent()
        {
            if (content == null)
                content = new ContentManager(ScreenManager.Game.Services, "Content");

            song.song = content.Load<Song>(song.AssetName);

            smokeParticleSystem = new SmokeParticleSystem(ScreenManager.Game);
            smokeParticleSystem.UpdateOrder = 100;
            smokeParticleSystem.DrawOrder = 100;
            particleSystemManager.AddParticleSystem(smokeParticleSystem);
            particleSystemManager.AutoInitializeAllParticleSystems(ScreenManager.Game.GraphicsDevice,
                content);
            smokeParticleSystem.Emitter.EmitParticlesAutomatically = false;

            beats = new Queue<TimeSpan>();
            foreach (Beat b in song.beats)
            {
                beats.Enqueue(b.beat);
            }
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(ScreenManager.Game.GraphicsDevice);
            font = content.Load<SpriteFont>("Courier New");

            //drawable area of the game screen.
            viewportRect = new Rectangle(0, 0,
                ScreenManager.Game.GraphicsDevice.Viewport.Width,
                ScreenManager.Game.GraphicsDevice.Viewport.Height);

            player1paddle = new GameObject(content.Load<Texture2D>("Sprites\\paddle"));
            player1paddle.position = new Vector2(viewportRect.Left, (viewportRect.Height / 2) - (player1paddle.sprite.Height / 2));

            player2paddle = new GameObject(content.Load<Texture2D>("Sprites\\paddle"));
            player2paddle.position = new Vector2(viewportRect.Right - player2paddle.sprite.Width, (viewportRect.Height / 2) - (player2paddle.sprite.Height / 2));


            ball = new GameObject(content.Load<Texture2D>("Sprites\\ball"));
            ball.position = new Vector2(player1paddle.GetRectangle().Right, player1paddle.GetRectangle().Bottom - player1paddle.GetRectangle().Height / 2);

            distance = (viewportRect.Width - player1paddle.sprite.Width - player2paddle.sprite.Width - ball.sprite.Width);

            SongPlayer.StartSong(song);
        }


        /// <summary>
        /// Unload graphics content used by the game.
        /// </summary>
        public override void UnloadContent()
        {
            content.Unload();
        }


        #endregion

        #region Update and Draw


        /// <summary>
        /// Updates the state of the game. This method checks the GameScreen.IsActive
        /// property, so the game will stop updating when the pause menu is active,
        /// or if you tab away to a different application.
        /// </summary>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
                                                       bool coveredByOtherScreen)
        {
            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

            if (IsActive)
            {
                UpdateParticles();
                UpdateGame(gameTime);
            }
        }

        public void UpdateGame(GameTime gameTime)
        {
            // Allows the game to exit
            KeyboardState keyboardState = Keyboard.GetState();

            UpdatePaddles();
            UpdateBall();
            particleSystemManager.UpdateAllParticleSystems((float)gameTime.ElapsedGameTime.TotalSeconds);

            Vector2 where = new Vector2(ball.GetRectangle().Center.X, ball.GetRectangle().Center.Y);
            if (ball.velocity.X > 0)
            {
                where.X = ball.GetRectangle().Left;
            }
            else if (ball.velocity.X < 0)
            {
                where.X = ball.GetRectangle().Right;
            }
            if (keyboardState.IsKeyDown(Keys.Space) && previousKeyboardState.IsKeyUp(Keys.Space))
            {
                TriggerSmokeParticle(new TimeSpan(0, 0, 5));
            }
            previousKeyboardState = keyboardState;
        }

        private void UpdateParticles()
        {
            UpdateSmokeParticles();
        }

        private void UpdateSmokeParticles()
        {
            if (particleSystemEndTimes.ContainsKey(smokeParticleSystem) 
                && DateTime.Now <= particleSystemEndTimes[smokeParticleSystem])
            {
                smokeParticleSystem.Emitter.PositionData.Position.X = ball.GetRectangle().Center.X;
                smokeParticleSystem.Emitter.PositionData.Position.Y = ball.GetRectangle().Center.Y; 
                ball.visible = false;
                smokeParticleSystem.AddParticle();
            }
            else if (ball.visible == false)
            {
                ball.visible = true;
            }
        }

        private void TriggerSmokeParticle(TimeSpan t)
        {
            particleSystemEndTimes[smokeParticleSystem] = DateTime.Now + t;
        }

        protected void UpdateBall()
        {
            Rectangle paddle1Rect = player1paddle.GetRectangle();
            Rectangle paddle2Rect = player2paddle.GetRectangle();
            Rectangle ballRect = ball.GetRectangle();
            if (MediaPlayer.State == MediaState.Playing)
            {
                Beat b = null;
                
                if (ballRect.Left <= paddle1Rect.Right && ballRect.Bottom >= paddle1Rect.Top && ballRect.Top <= paddle1Rect.Bottom)
                {
                    float diff = ballRect.Center.Y - paddle1Rect.Center.Y;
                    ball.velocity.Y += diff / 15;
                    b = SongPlayer.FindNextBeat(song);
                    TimeSpan time = b.beat - SongPlayer.PlayPosition();
                    float seconds = (float)time.TotalSeconds;
                    ballSpeed = (distance / seconds) / 60.0f; // frame rate 60fps
                    ball.position.X = paddle1Rect.Right;
                }
                else if (ballRect.Right >= paddle2Rect.Left && ballRect.Bottom >= paddle2Rect.Top && ballRect.Top <= paddle2Rect.Bottom)
                {
                    float diff = ballRect.Center.Y - paddle2Rect.Center.Y;
                    ball.velocity.Y += diff / 15;
                    b = SongPlayer.FindNextBeat(song);
                    TimeSpan time = b.beat - SongPlayer.PlayPosition();
                    float seconds = (float)time.TotalSeconds;
                    ballSpeed = (distance / seconds) / 60.0f; // frame rate 60fps
                    ballSpeed *= -1;
                    ball.position.X = paddle2Rect.Left - ballRect.Width;
                }

                if (ballRect.Top <= viewportRect.Top || ballRect.Bottom >= viewportRect.Bottom)
                {
                    ball.velocity.Y *= -1;
                }

                ball.velocity.Y = MathHelper.Clamp(ball.velocity.Y, -maxVerticalBallVelocity, maxVerticalBallVelocity);
                paddle1Rect = player1paddle.GetRectangle();
                paddle2Rect = player2paddle.GetRectangle();
                ballRect = ball.GetRectangle();

                if (ballRect.Left < paddle1Rect.Right)
                {
                    player2score++;
                    b = SongPlayer.FindNextBeat(song);
                    TimeSpan time = b.beat - SongPlayer.PlayPosition();
                    float seconds = (float)time.TotalSeconds;
                    ballSpeed = (distance / seconds) / 60.0f; // frame rate 60fps
                    ballSpeed *= -1;
                    ball.position.X = paddle2Rect.Left - ballRect.Width;
                    ball.position.Y = paddle2Rect.Top + (paddle2Rect.Height / 2) - (ballRect.Height / 2);
                    ball.velocity.Y = 0.0f;
                }
                else if (ballRect.Right > paddle2Rect.Left)
                {
                    player1score++;
                    b = SongPlayer.FindNextBeat(song);
                    TimeSpan time = b.beat - SongPlayer.PlayPosition();
                    
                    float seconds = (float)time.TotalSeconds;
                    ballSpeed = (distance / seconds) / 60.0f; // frame rate 60fps
                    ball.position.X = paddle1Rect.Right;
                    ball.position.Y = paddle1Rect.Top + (paddle1Rect.Height / 2) - (ballRect.Height / 2);
                    ball.velocity.Y = 0.0f;
                }
                if (b != null)
                {
                    if (previousBeat.Effect.Contains("smoke"))
                    {
                        TriggerSmokeParticle(previousBeat.EffectDuration);
                    }
                    previousBeat = b;
                }
                
                ball.velocity.X = ballSpeed;

                ball.position += ball.velocity;
            }
        }

        protected void UpdatePaddles()
        {
            KeyboardState keyboardState = Keyboard.GetState();

            Rectangle paddle1Rect = player1paddle.GetRectangle();
            Rectangle paddle2Rect = player2paddle.GetRectangle();

            if (keyboardState.IsKeyDown(Keys.W) && keyboardState.IsKeyUp(Keys.S) &&
                paddle1Rect.Top > viewportRect.Top)
            {
                player1paddle.velocity.Y = -paddleSpeed;
            }
            else if (keyboardState.IsKeyDown(Keys.S) && keyboardState.IsKeyUp(Keys.W) &&
                paddle1Rect.Bottom < viewportRect.Bottom)
            {
                player1paddle.velocity.Y = paddleSpeed;
            }
            else
            {
                player1paddle.velocity = Vector2.Zero;
            }

            if ((keyboardState.IsKeyDown(Keys.Up) && keyboardState.IsKeyUp(Keys.Down))
                || (paddle2Rect.Top > ball.GetRectangle().Top)
                && paddle2Rect.Top > viewportRect.Top)
            {
                player2paddle.velocity.Y = -paddleSpeed;
            }
            else if ((keyboardState.IsKeyDown(Keys.Down) && keyboardState.IsKeyUp(Keys.Up))
                || (paddle2Rect.Bottom < ball.GetRectangle().Bottom)
                &&
                paddle2Rect.Bottom < viewportRect.Bottom)
            {
                player2paddle.velocity.Y = paddleSpeed;
            }
            else
            {
                player2paddle.velocity = Vector2.Zero;
            }

            player1paddle.position += player1paddle.velocity;
            player2paddle.position += player2paddle.velocity;
        }
        
        public static GameSong LoadSong(string xmlpath)
        {
            return LoadSong(xmlpath, null);
        }

        public static GameSong LoadSong(string xmlpath, ContentManager c)
        {
            GameSong song = new GameSong();

            XmlSerializer serializer = new XmlSerializer(typeof(GameSong));
            StreamReader reader = new StreamReader(xmlpath);

            try
            {
                song = (GameSong)serializer.Deserialize(reader);
            }
            catch
            {
            }
            finally
            {
                reader.Close();
                if (c != null)
                {
                    song.song = c.Load<Song>(song.AssetName);
                }
            }
            return song;
        }

        /// <summary>
        /// Lets the game respond to player input. Unlike the Update method,
        /// this will only be called when the gameplay screen is active.
        /// </summary>
        public override void HandleInput(InputState input)
        {
            if (input == null)
                throw new ArgumentNullException("input");

            // Look up inputs for the active player profile.
            int playerIndex = (int)ControllingPlayer.Value;

            KeyboardState keyboardState = input.CurrentKeyboardStates[playerIndex];
            GamePadState gamePadState = input.CurrentGamePadStates[playerIndex];

            // The game pauses either if the user presses the pause button, or if
            // they unplug the active gamepad. This requires us to keep track of
            // whether a gamepad was ever plugged in, because we don't want to pause
            // on PC if they are playing with a keyboard and have no gamepad at all!
            bool gamePadDisconnected = !gamePadState.IsConnected &&
                                       input.GamePadWasConnected[playerIndex];

            if (input.IsPauseGame(ControllingPlayer) || gamePadDisconnected)
            {
                SongPlayer.PauseSong();
                ScreenManager.AddScreen(new PauseMenuScreen(), ControllingPlayer);
            }
            else
            {
                if (SongPlayer.isPaused())
                {
                    SongPlayer.ResumeSong();
                }
                // TODO: Handle player input here
            }
        }

        /// <summary>
        /// Draws the gameplay screen.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            // This game has a blue background. Why? Because!
            ScreenManager.GraphicsDevice.Clear(ClearOptions.Target,
                                               Color.CornflowerBlue, 0, 0);
            
            particleSystemManager.DrawAllParticleSystems();
            DrawGame();
        }

        public void DrawGame()
        {
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
            DrawScore(spriteBatch);

            if (ball.visible)
            {
                spriteBatch.Draw(ball.sprite, ball.position, null, Color.White, ball.rotation, Vector2.Zero,
                    1.0f, SpriteEffects.None, 0);
            }

            if (player1paddle.visible)
            {
                spriteBatch.Draw(player1paddle.sprite, player1paddle.position, null, Color.White,
                    player1paddle.rotation, Vector2.Zero, 1.0f, SpriteEffects.None, 0);
            }

            if (player2paddle.visible)
            {
                spriteBatch.Draw(player2paddle.sprite, player2paddle.position, null, Color.White,
                    player2paddle.rotation, Vector2.Zero, 1.0f, SpriteEffects.None, 0);
            }

            spriteBatch.End();
        }

        public void DrawScore(SpriteBatch spriteBatch)
        {
            string score = String.Format("Player 1: {0}  Player 2: {1}  {2}", player1score, player2score,
                SongPlayer.PlayPosition().ToString());

            spriteBatch.DrawString(font, score, scoreDrawPoint, Color.Black, 0, Vector2.Zero, 1.0f,
                SpriteEffects.None, 0.5f);
        }



        #endregion
    }
}
